// BonusBingoSubGame.cpp: implementation of the BonusBingoSubGame class.
//
//////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <math.h>
#include "lib.h"
#include "BonusBingoConstants.h"
#include "BonusBingoSubGame.h"
#include "getrand.h"

#ifdef DEBUG_KNOWNSPINS
#include "BonusBingoGuts.h"
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
using namespace BonusBingo;

WeightedOutcomes BonusBingoSubGame::s_SubGameRoundsOutcomes;

const int32 BonusBingoSubGame::JokerDieValue( 10 );
const int32 BonusBingoSubGame::NumberOfDice(5);
const int32 BonusBingoSubGame::SubgameWinThreshold(BonusBingo::BONUS_ROUND_JOKERS_NEEDED);

BonusBingoSubGame::BonusBingoSubGame(const String& pid, bool8 forMoney)
{
    if ( s_SubGameRoundsOutcomes.empty() )
    {
        LoadOutcomes();
    }

    //TODO - add a dang const correct constructor to SubGame.
    m_subGame = new SubGame( 'K', const_cast<String&>(pid), 0, forMoney );

    ZeroMemory( m_gid, sizeof( m_gid ) );

    if ( m_subGame )
    {
        m_subGame->update( BBSGIDX_STARTED, 0 );
        m_subGame->update( BBSGIDX_NUM_JOKERBALLS, 0 );
        m_subGame->update( BBSGIDX_CURRENT_ROLL, 0 );
        m_subGame->update( BBSGIDX_LAST_ROLL_CREDITS_WON, 0 );
    }
}

BonusBingoSubGame::~BonusBingoSubGame()
{
    delete m_subGame;
}

void BonusBingoSubGame::setGameID(const GameID ID )
{
    memcpy( m_gid, ID, sizeof( m_gid ) );
}

const GameID& BonusBingoSubGame::getGameID() const
{
    return m_gid;
}

SubGame& BonusBingoSubGame::getSubGameData() const
{
    ASSERT( m_subGame );
    return *m_subGame;
}

void BonusBingoSubGame::LoadOutcomes()
{
    s_SubGameRoundsOutcomes.insert( std::make_pair( ONE_GAME, 1 ) );
    s_SubGameRoundsOutcomes.insert( std::make_pair( TWO_GAMES, 2 ) );
    s_SubGameRoundsOutcomes.insert( std::make_pair( THREE_GAMES, 3 ) );
}

void BonusBingoSubGame::start()
{
    m_subGame->update( BBSGIDX_STARTED, 1 );
}

void BonusBingoSubGame::reset()
{
    ZeroMemory( m_gid, sizeof( m_gid ) );
    
    for ( int i = 0; i < m_subGame->getDataCount(); i++ )
    {
        m_subGame->update( i, 0 );
    }
}

bool8 BonusBingoSubGame::started() const
{
    return 1 == m_subGame->getData( BBSGIDX_STARTED );
}

void BonusBingoSubGame::selectNumberOfRolls() 
{
    int32 rollsOutcome = 0;

#ifdef DEBUG_KNOWNSPINS
    if ( BonusBingoGuts::Random != 
         BonusBingoGuts::GetSubgameOutcomeValue( BonusBingoGuts::BONUS_GAME_COUNT ) )
    {
        rollsOutcome = BonusBingoGuts::GetSubgameOutcomeValue( BonusBingoGuts::BONUS_GAME_COUNT );
    }
    else    //Just run the normal course

#endif
    rollsOutcome = getOutcome( s_SubGameRoundsOutcomes, getrand() % PROBABILITY_BASE ); 

    m_subGame->update( BBSGIDX_NUM_ROLLS_TOTAL, rollsOutcome );
}

bool8 BonusBingoSubGame::finished() const
{
    return getCurrentRoll() >= getTotalRolls();
}

void BonusBingoSubGame::rollDice()
{
    int32 creditsWon = 0;

    int32 diceRoll = 0;

    for( int die = 0; die < NumberOfDice; ++die )
    {
#ifdef DEBUG_KNOWNSPINS
        int32 outcomeKey = BonusBingoGuts::BONUS_GAME_DIE_VALUES + die;
        if ( BonusBingoGuts::Random != BonusBingoGuts::GetSubgameOutcomeValue( outcomeKey ) )
        {
            diceRoll = BonusBingoGuts::GetSubgameOutcomeValue( outcomeKey );
        }
        else        //Normal path
#endif
            diceRoll = getrand() % 6 + 1;

        creditsWon += creditValue( diceRoll );

        m_subGame->update( BBSGIDX_DIE_VALUES + die, diceRoll );
    }

    incrementCurrentRoll();
    updateCreditsForLastRoll( creditsWon );
    updateTotalCredits( creditsWon );

}

int32 BonusBingoSubGame::getAverageBetMultiplier() const
{
    return m_subGame->getData(BBSGIDX_AVG_JOKER_BET);
}

int32 BonusBingoSubGame::getCurrentDieValue( int32 dieIndex ) const
{
    ASSERT( 0 <= dieIndex && dieIndex < NumberOfDice );

    return m_subGame->getData( BBSGIDX_DIE_VALUES + dieIndex );
}

int32 BonusBingoSubGame::getTotalRolls()   const
{
    return m_subGame->getData( BBSGIDX_NUM_ROLLS_TOTAL );
}

void BonusBingoSubGame::incrementCurrentRoll()
{
    m_subGame->update( BBSGIDX_CURRENT_ROLL, getCurrentRoll() + 1 );
}

void BonusBingoSubGame::incrementJokerBalls(int32 lastBet)
{
    int32 oldTotal = getAverageBetMultiplier() * getCurrentJokerBallCount();

    m_subGame->update( BBSGIDX_NUM_JOKERBALLS, getCurrentJokerBallCount() + 1 );

    //Update the rolling average, too
    int32 newAverage = ceil( (double)( oldTotal + lastBet ) / (double)getCurrentJokerBallCount() );

    //And make it an even 5 multiple
    if ( newAverage % 5 
        && getCurrentJokerBallCount() == BonusBingo::BONUS_ROUND_JOKERS_NEEDED )
    {
        newAverage += ( 5 - ( newAverage % 5 ) );
    }

    m_subGame->update( BBSGIDX_AVG_JOKER_BET, newAverage );
}

int32 BonusBingoSubGame::getCurrentJokerBallCount() const
{
    return m_subGame->getData( BBSGIDX_NUM_JOKERBALLS );
}

int32 BonusBingoSubGame::getCurrentRoll() const
{
    return m_subGame->getData( BBSGIDX_CURRENT_ROLL );
}

void BonusBingoSubGame::updateTotalCredits( const int32 credits )
{
    m_subGame->update( BBSGIDX_TOTAL_CREDITS_WON, getTotalCredits() + credits );
}

int32 BonusBingoSubGame::getTotalCredits() const
{
    return m_subGame->getData( BBSGIDX_TOTAL_CREDITS_WON );
}

int32 BonusBingoSubGame::creditValue( const int32 diceValue )  const
{
    int32 baseValue = diceValue > 1 ? diceValue : JokerDieValue;

    return baseValue * getAverageBetMultiplier();
}

void BonusBingoSubGame::updateCreditsForLastRoll( int32 credits )
{
    m_subGame->update(BBSGIDX_LAST_ROLL_CREDITS_WON, credits );
}

int32 BonusBingoSubGame::getCreditsForLastRoll() const
{
    return m_subGame->getData( BBSGIDX_LAST_ROLL_CREDITS_WON );
}